/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.zattix.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;
import javax.ejb.EJB;
import javax.jms.Session;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import org.expressme.openid.*;
import org.omg.PortableServer.REQUEST_PROCESSING_POLICY_ID;
import org.zattix.bean.User;
import org.zattix.ejb.UserFacade;
import org.zattix.util.Logger;

/**
 *
 * @author s.rainoldi
 */
@WebServlet(name = "ServletLogin", urlPatterns = {"/login"})
public class ServletLogin extends HttpServlet {

    @EJB
    private UserFacade userFacade;
    static final long ONE_HOUR = 3600000L;
    static final long EXPIRING_HOURS = ONE_HOUR * 48L;
    static final String ATTR_MAC = "openid_mac";
    static final String ATTR_ALIAS = "openid_alias";
    private OpenIdManager manager;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpSession session = request.getSession();
        ServletContext application = session.getServletContext();
        
        manager = new OpenIdManager();
        manager.setRealm((String) application.getAttribute("server_home"));
        manager.setReturnTo((String) application.getAttribute("server_home") + "login");



        String op = request.getParameter("op");
        if (op == null) {
            // check sign on result from Google or Yahoo:
            checkNonce(request.getParameter("openid.response_nonce"));
            System.out.println(request.getParameter("openid.response_nonce"));
            // get authentication:
            byte[] mac_key = (byte[]) request.getSession().getAttribute(ATTR_MAC);
            String alias = (String) request.getSession().getAttribute(ATTR_ALIAS);
            Authentication authentication = manager.getAuthentication(request, mac_key, alias);
            response.setContentType("text/html; charset=UTF-8");
            showAuthentication(response.getWriter(), authentication);
            User user = userFacade.getUserByEmail(authentication.getEmail());
            if (user == null) {
                user = new User();
                user.setUsername(authentication.getFirstname());
                user.setEmail(authentication.getEmail());
                user.setNonce(request.getParameter("openid.response_nonce"));
                userFacade.create(user);
            } else {
                user.setNonce(request.getParameter("openid.response_nonce"));
                userFacade.edit(user);
            }
            session.setAttribute("openid", user.getNonce());
            Cookie cookie = new Cookie("openid", user.getNonce());
            cookie.setMaxAge(60 * 60 * 24 * 2); // Due giorni
            response.addCookie(cookie);
            Logger.log(authentication.getEmail() + " logged in (OpenID Google)");
            response.sendRedirect((String) application.getAttribute("server_home"));

        } else if (op.equals("Google")) {
            // redirect to Google  sign on page:
            Endpoint endpoint = manager.lookupEndpoint(op);
            Association association = manager.lookupAssociation(endpoint);
            request.getSession().setAttribute(ATTR_MAC, association.getRawMacKey());
            request.getSession().setAttribute(ATTR_ALIAS, endpoint.getAlias());
            String url = manager.getAuthenticationUrl(endpoint, association);
            response.sendRedirect(url);
        } else {
            throw new ServletException("Unsupported OP: " + op);
        }

    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP
     * <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

    private void checkNonce(String nonce) {
        // check response_nonce to prevent replay-attack:
        if (nonce == null || nonce.length() < 20) {
            throw new OpenIdException("Verify failed.");
        }
        // make sure the time of server is correct:
        long nonceTime = getNonceTime(nonce);
        long diff = Math.abs(System.currentTimeMillis() - nonceTime);
        if (diff > ONE_HOUR) {
            throw new OpenIdException("Bad nonce time.");
        }
        if (isNonceExist(nonce)) {
            throw new OpenIdException("Verify nonce failed.");
        }
        storeNonce(nonce, nonceTime + EXPIRING_HOURS);
    }

    private void showAuthentication(PrintWriter pw, Authentication auth) {
    }

    long getNonceTime(String nonce) {
        try {
            return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(nonce.substring(0, 19) + "+0000").getTime();
        } catch (ParseException e) {
            throw new OpenIdException("Bad nonce time.");
        }
    }
    // simulate a database that store all nonce:
    private Set<String> nonceDb = new HashSet<String>();

    // check if nonce is exist in database:
    boolean isNonceExist(String nonce) {
        return nonceDb.contains(nonce);
    }

    // store nonce in database:
    void storeNonce(String nonce, long expires) {
        nonceDb.add(nonce);
    }
}
